package com.radar.controller;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.exceptions.ExceptionUtil;
import cn.hutool.core.io.FileUtil;
import cn.hutool.core.util.ZipUtil;
import com.radar.algorithm.MathCommon;
import com.radar.algorithm.Storms;
import com.radar.common.*;
import com.radar.exception.RadarCaclError;
import com.radar.exception.RadarDecodeError;
import com.radar.io.CinradRadar;
import com.radar.io.RadarGridData;
import com.radar.model.ResultBody;
import com.radar.util.IOCommon;
import com.radar.util.RadarMap;
import com.radar.utils.RefObj;
import com.radar.visualize.PolarMap;
import com.radar.visualize.RasterMap;
import com.radar.visualize.VCSMap;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.meteoinfo.global.Extent;
import org.meteoinfo.global.PointD;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import springfox.documentation.annotations.ApiIgnore;
import sun.misc.BASE64Encoder;
import ucar.ma2.InvalidRangeException;

import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.List;
import java.util.concurrent.ExecutionException;

@Api(tags = "2.0 雷达接口")
@RestController
@RequestMapping("radr")
@Slf4j
public class RadarController {

    @Autowired
    private HttpServletRequest request;

    @ApiOperation("2.0.打开雷达文件")
    @GetMapping("/open")
    @ApiImplicitParam(name = "file", value = "文件路径", defaultValue = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", example = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", paramType = "query", required = true, dataType = "String")
    public ResultBody open(@ApiIgnore @RequestParam String file) {
        file = file.indexOf("?") == -1 ? file : file.substring(0, file.indexOf("?"));
        String sessionId = request.getSession().getId();
        CinradRadar cinradRadar = null;
        try {
            cinradRadar = RadarMap.cacheRadar(sessionId, file);
            Map<String, Object> result = new HashMap<>();
            result.put("code", cinradRadar.code);
            result.put("alt", cinradRadar.alt);
            result.put("radarSite", cinradRadar.radar.getRadarSite());
            result.put("date", DateUtil.format(cinradRadar.radar.dateTime, DateConstants.DATE_TIME_FORMAT_YYYY_MM_DD_HH_MI_SS));
            result.put("type", cinradRadar.type.name());
            result.put("dataType", cinradRadar.datType.name());
            result.put("vcp", cinradRadar.radar.vcp);
            result.put("elevation", cinradRadar.radar.getElevation());
            result.put("rresolution", cinradRadar.radar.getResolution(ProductType.REF));
            result.put("vresolution", cinradRadar.radar.getResolution(ProductType.VEL));
            cinradRadar.radar.flush();
            return ResultBody.ok().data(result);
        } catch (ExecutionException e) {
            log.error(ExceptionUtil.getSimpleMessage(e));
        } catch (Exception radarDecodeError) {
            log.error(ExceptionUtil.getSimpleMessage(radarDecodeError));
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
        }
        return ResultBody.failed();
    }

    @ApiOperation("2.1.加载雷达产品")
    @GetMapping("/product")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件路径", defaultValue = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", example = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "type", value = "产品类别(REF,VEL,CR,ET,VIL)", defaultValue = "REF", example = "REF", paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "index", value = "ref、vel的 index", defaultValue = "0", example = "0", paramType = "query", required = true, dataType = "int")
    })
    public ResultBody product(@ApiIgnore @RequestParam String file, @ApiIgnore @RequestParam String type, @RequestParam int index) {
        file = file.indexOf("?") == -1 ? file : file.substring(0, file.indexOf("?"));
        String sessionId = request.getSession().getId();
        ProductType productType = ProductType.valueOf(type);
        if (productType == null) {
            log.error("雷达产品参数有误");
            return ResultBody.failed();
        }
        CinradRadar cinradRadar = null;
        try {
            cinradRadar = RadarMap.cacheRadar(sessionId, file);
            String radarPath = File.separator + cinradRadar.radar.getRadarSite().getStation() +
                    File.separator + new File(file).getName() + File.separator + productType.name() + "_" + index + ".png";
            String imgPath = IOCommon.getTmpDir() + File.separator + "cinradar_tmp" + radarPath;
            if (FileUtil.exist(imgPath)) {
                Extent extent = getExtent(productType, cinradRadar, index);
                cinradRadar.radar.flush();
                return ResultBody.ok().data(radarPath).put("extent", extent);
            }
            if (!FileUtil.exist(new File(imgPath).getParentFile())) {
                FileUtil.mkdir(imgPath);
            }
            if (productType == ProductType.REF || productType == ProductType.VEL) {
                PolarMap polarMap = new PolarMap(cinradRadar.radar);
                PolarMap.PolarInfo polarInfo = PolarMap.buildPolarInfo(index, productType);
                polarInfo.backColor(new Color(0, 0, 0, 0));
                BufferedImage bufferedImage = polarMap.simplePolar(polarInfo);
                ImageIO.write(bufferedImage, "png", new File(imgPath));
            } else {
                RasterMap rasterMap = new RasterMap(cinradRadar.radar);
                RasterMap.RasterInfo rasterInfo = RasterMap.buildRasterInfo(productType);
                rasterInfo.backColor(new Color(0, 0, 0, 0));
                RadarGridData data = null;
                if (ProductType.CR == productType) {
                    data = cinradRadar.radar.getCR();
                } else if (ProductType.ET == productType) {
                    data = cinradRadar.radar.getET();
                } else if (ProductType.VIL == productType) {
                    data = cinradRadar.radar.getVIL();
                }
                BufferedImage bufferedImage = rasterMap.simpleRaster(rasterInfo, data);
                ImageIO.write(bufferedImage, "png", new File(imgPath));
            }
            Extent extent = getExtent(productType, cinradRadar, index);
            cinradRadar.radar.flush();
            return ResultBody.ok().data(radarPath).put("extent", extent);
        } catch (Exception e) {
            log.error(ExceptionUtil.getSimpleMessage(e));
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
        }
        return ResultBody.failed();
    }

    @ApiOperation("2.2.单点雷达数据")
    @GetMapping("/point")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件路径", defaultValue = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", example = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "index", value = "ref、vel的 index", defaultValue = "0", example = "0", paramType = "query", required = true, dataType = "int"),
            @ApiImplicitParam(name = "lon", value = "lon", defaultValue = "0", example = "0", paramType = "query", required = true, dataType = "int"),
            @ApiImplicitParam(name = "lat", value = "lat", defaultValue = "0", example = "0", paramType = "query", required = true, dataType = "int")
    })
    public ResultBody point(@ApiIgnore @RequestParam String file, @RequestParam int index, @RequestParam double lon, @RequestParam double lat) {
        file = file.indexOf("?") == -1 ? file : file.substring(0, file.indexOf("?"));
        String sessionId = request.getSession().getId();
        CinradRadar cinradRadar = null;
        try {
            cinradRadar = RadarMap.cacheRadar(sessionId, file);
            double ref = cinradRadar.radar.point(lon, lat, ProductType.REF, index);
            double vel = cinradRadar.radar.point(lon, lat, ProductType.VEL, index);
            double sw = cinradRadar.radar.point(lon, lat, ProductType.SW, index);
            cinradRadar.radar.close();
            return ResultBody.ok().put("ref", ref).put("vel", vel).put("sw", sw);
        } catch (Exception e) {
            log.error(ExceptionUtil.getSimpleMessage(e));
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
        }
        return ResultBody.failed();
    }

    @ApiOperation("2.3.剖面")
    @GetMapping("/vcs")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件路径", defaultValue = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", example = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "type", value = "产品类别(REF,VEL,CR,ET,VIL)", defaultValue = "REF", example = "REF", paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "points", value = "点集合", defaultValue = "points", example = "110-27,111-27", paramType = "query", required = true, dataType = "String")
    })
    public ResultBody vcs(@ApiIgnore @RequestParam String file, @RequestParam String points, @RequestParam String type){
        file = file.indexOf("?") == -1 ? file : file.substring(0, file.indexOf("?"));
        String sessionId = request.getSession().getId();
        CinradRadar cinradRadar = null;
        try {
            cinradRadar = RadarMap.cacheRadar(sessionId, file);
            List<PointD> list = new ArrayList<>();
            Arrays.asList(points.split(";")).forEach(i -> {
                list.add(new PointD(Double.parseDouble(i.split(":")[0]),Double.parseDouble(i.split(":")[1])));
            });
            VCSMap vcsMap = new VCSMap(cinradRadar.radar);
            VCSMap.VCSInfo vcsInfo = vcsMap.buildRasterInfo(ProductType.valueOf(type))
                    .altNum(100)
                    .pointNum(500)
                    .maxAlt(22);
            BufferedImage bufferedImage = vcsMap.simpleVCS(vcsInfo, list);
            cinradRadar.radar.close();
            return ResultBody.ok().data(imageToBase64(bufferedImage));
        } catch (Exception e) {
            log.error(ExceptionUtil.getSimpleMessage(e));
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
        }
        return ResultBody.failed();
    }

    @ApiOperation("2.4.三维体数据")
    @GetMapping("/surface3D")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件路径", defaultValue = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", example = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", paramType = "query", required = true, dataType = "String"),
            @ApiImplicitParam(name = "type", value = "产品类别(REF,VEL,CR,ET,VIL)", defaultValue = "REF", example = "REF", paramType = "query", required = true, dataType = "String")
    })
    public ResultBody surface3D(@ApiIgnore @RequestParam String file, @RequestParam String type){
        file = file.indexOf("?") == -1 ? file : file.substring(0, file.indexOf("?"));
        String sessionId = request.getSession().getId();
        CinradRadar cinradRadar = null;
        try {
            cinradRadar = RadarMap.cacheRadar(sessionId, file);
            String radarPath = File.separator + cinradRadar.radar.getRadarSite().getStation() +
                    File.separator + new File(file).getName() + File.separator + "surface.dat";
            String rPath = IOCommon.getTmpDir() + File.separator + "cinradar_tmp" + radarPath;
            if(FileUtil.exist(rPath + ".surface")){
                return ResultBody.ok().data(radarPath + ".surface");
            }
            double[] alt = new double[]{1000, 1500, 2000, 2500, 3000, 4000, 5000, 6000, 7000, 8000, 9000, 10000, 11000, 12000};
            byte[][][] cappi = cinradRadar.radar.cappi(ProductType.valueOf(type), alt);
            double jself = 500.0 / cappi[0].length;
            double kself = 500.0 / cappi[0][0].length;
            byte[] bytes = new byte[alt.length * 500 * 500];
            int index = 0;
            for (int i = 0; i < alt.length; i++) {
                for (int j = 0; j < 500; j++) {
                    for (int k = 0; k < 500; k++) {
                        int jj = (int) Math.floor((500 - 1 - j) / jself);
                        int kk = (int) Math.floor(k / kself);
                        if (cappi[i][jj][kk] == -128 || MathCommon.DecodeRef(cappi[i][jj][kk]) <= 5) {
                            bytes[index] = (byte) 255;
                        } else {
                            bytes[index] = (byte) MathCommon.DecodeRef(cappi[i][jj][kk]);
                        }
                        index++;
                    }
                }
            }
            File f = new File(rPath);
            if (f.exists()) {
                f.delete();
            }
            FileUtil.writeBytes(bytes, f);
            ZipUtil.zip(f.getAbsolutePath(), f.getAbsolutePath() + ".surface");
            if (f.exists()) {
                f.delete();
            }
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
            return ResultBody.ok().data(radarPath + ".surface");
        } catch (Exception e) {
            log.error(ExceptionUtil.getSimpleMessage(e));
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
        }
        return ResultBody.failed();
    }


    @ApiOperation("2.5.风暴追踪")
    @GetMapping("/storms")
    @ApiImplicitParams({
            @ApiImplicitParam(name = "file", value = "文件路径", defaultValue = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", example = "/Users/xx/data/micaps/SINGLERADAR/ARCHIVES/PRE_QC/Z9836/Z_RADR_I_Z9836_20190730022600_O_DOR_CA_CAP.bin.bz2", paramType = "query", required = true, dataType = "String")
    })
    public ResultBody storms(@ApiIgnore @RequestParam String file){
        file = file.indexOf("?") == -1 ? file : file.substring(0, file.indexOf("?"));
        String sessionId = request.getSession().getId();
        CinradRadar cinradRadar = null;
        try {
            cinradRadar = RadarMap.cacheRadar(sessionId, file);
            RadarGridData cr = cinradRadar.radar.getCR();
            double[][] decode = MathCommon.decode(cr.radarData, ProductType.REF);
            int step = 2;
            double[][] data = new double[decode.length / step][decode[0].length / step];
            for (int i = 0; i < data.length; i++) {
                for (int j = 0; j < data[0].length; j++) {
                    data[i][j] = decode[i * step][j * step];
                }
            }
            RefObj<Integer> count = new RefObj<>(0);
            List<Storms.StormDt> list = Storms.getStorms(data, step, count, 32, cinradRadar);
            list.forEach(z -> {
                z.setCenter(new PointD(cr.xArray[(int) z.getCenter().Y],cr.yArray[cr.yArray.length - 1 - (int) z.getCenter().X]));
                z.getBoundary().forEach(point -> {
                    double x = cr.xArray[(int) point.Y];
                    double y = cr.yArray[cr.yArray.length - 1 - (int) point.X];
                    point.X = x;
                    point.Y = y;
                });
                z.getContain().forEach(point -> {
                    double x = cr.xArray[(int) point.Y];
                    double y = cr.yArray[cr.yArray.length - 1 - (int) point.X];
                    point.X = x;
                    point.Y = y;
                });
            });
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
            return ResultBody.ok().put("storms", list);
        } catch (Exception e) {
            log.error(ExceptionUtil.getSimpleMessage(e));
            if (cinradRadar.radar != null) {
                cinradRadar.radar.close();
            }
        }
        return ResultBody.failed();
    }

    public Extent getExtent(ProductType type, CinradRadar cinradRadar, int index) throws RadarCaclError, RadarDecodeError, IOException, InvalidRangeException {
        Extent extent = null;
        double lon = cinradRadar.radar.getRadarSite().getLon();
        double lat = cinradRadar.radar.getRadarSite().getLat();
        if (type == ProductType.REF || type == ProductType.VEL) {
            double res = cinradRadar.radar.getResolution(type);
            byte[][] raw = cinradRadar.radar.getRaw(index, type);
            double ele = cinradRadar.radar.getElevation()[index];
            double distance = raw[0].length * res;
            distance = Math.cos(ele * Const.DEG2RAD) * distance;
            extent = Common.getExtentRadius(lon, lat, distance);
        } else if (type == ProductType.CR) {
            extent = cinradRadar.radar.getCR().getExtent();
        } else if (type == ProductType.ET) {
            extent = cinradRadar.radar.getET().getExtent();
        } else if (type == ProductType.VIL) {
            extent = cinradRadar.radar.getVIL().getExtent();
        }
        return extent;
    }

    public String imageToBase64(BufferedImage br) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();//io流
        ImageIO.write(br, "png", baos);//写入流中
        byte[] bytes = baos.toByteArray();//转换成字节
        BASE64Encoder encoder = new BASE64Encoder();
        String pngBase64 = encoder.encodeBuffer(bytes).trim();//转换成base64串
        pngBase64 = pngBase64.replaceAll("\n", "").replaceAll("\r", "");//删除 \r\n
        return pngBase64;
    }
}
